#include "frame-builder.h"

static xml_node_t *type_root = NULL;

#warning: Initialize root node


	/*
	* format expected:
	* <root_node>
	* <data_type>
	*	<...>
	*	<definition>
			<sub_type>..		// sub_type attributes: array(elems), basic(len)
			<sub_type2>
			<...>
		</definition>
	* </data_type>
	* <data_type2>
	*	....
	* </data_type2>
	* </root_node>

	*/

/*
* Get the length of the data structure in bits
*/
static uint16_t get_type_length(const xml_node_t *node)
{
	uint16_t len = 0;
	uint8_t type_name_buffer[100];
	uint8_t attr_name_buf[30];
	xml_node_t definition_node;
	xml_node_t temp_node;
	BOOL found = FALSE;
	BOOL continue_parsing = FALSE;

	// search for <definition> node in children
	continue_parsing = xml_get_first_child(node, &definition_node);
	while (continue_parsing) {
		xml_get_node_name(&definition_node, type_name_buffer, (sizeof(type_name_buffer) - 1));
		if (strcmp(type_name_buffer, "definition") == 0) {
			found = TRUE;
			break;
		} else {
			continue_parsing = xml_get_next_sibling(&definition_node, &temp_node);
			definition_node = temp_node;
		}
	}

	if (!found) {
		// <definition> node not found in <type> schema
		printf("Error! Definition node not found\n");
		return 0;
	}

	continue_parsing = xml_get_first_child(&definition_node, &temp_node);
	while (continue_parsing) {
		uint16_t sub_type_len = 0;
		xml_get_node_name(&temp_node, type_name_buffer, (sizeof(type_name_buffer) - 1));
		if (strcmp(type_name_buffer, "basic") == 0) {
			// basic type. Get length from the len attribute
			xml_get_node_attribute(&temp_node, (uint8_t *)"len", attr_name_buf, (sizeof(attr_name_buf) - 1), "0");
			sub_type_len = NAME_TO_NUMERIC(attr_name_buf);
		} else {
			sub_type_len = get_type_length(&temp_node);
		}

		if (sub_type_len == 0) {
			printf("Error: 0 length type found!!\n");
			return 0;
		}

		// Check if the sub-type has an array specification
		xml_get_node_attribute(&temp_node, (uint8_t *)"elems", attr_name_buf, (sizeof(attr_name_buf) - 1), "1");
		len += sub_type_len * NAME_TO_NUMERIC(attr_name_buf);

		// Check for next field in the type
		xml_node_t next_node;
		continue_parsing = xml_get_next_sibling(&temp_node, &next_node);
		temp_node = next_node;
	}

	return len;
}

// ICM: 9311971077
static BOOL get_type_node_from_name(const uint8_t *type_name, xml_node_t *node)
{
	BOOL continue_parsing = FALSE;
	xml_node_t temp_node;
	uint8_t type_name_buffer[100];

	continue_parsing = xml_get_first_child(&type_root, &temp_node);
	while (continue_parsing) {
		xml_get_node_name(&temp_node, type_name_buffer, (sizeof(type_name_buffer) - 1));
		if (strcmp(type_name_buffer, type_name) == 0) {
			// found the node
			*node = temp_node;
			return TRUE;
		}
		xml_node_t temp2_node;
		continue_parsing = xml_get_next_sibling(&temp_node, &temp2_node);
		temp_node = temp2_node;
	}
		
	return FALSE;	// Node not found in the document
}

/*
* While building a frame buffer, the function appends the data type default contents at the 
* specified position (and bit offset) in the buffer
* The bit_offset finally reflects the new bit offset in the last byte after filling the type data.
* The functionr returns the pointer to the next byte after filling the data.
*/
static uint8_t *append_type_data_to_buffer(const xml_node_t *node, uint8_t *frame_buf, uint8_t *bit_offset)
{
	uint16_t len = 0;
	uint8_t type_name_buffer[100];
	uint8_t attr_name_buf[30];
	xml_node_t definition_node;
	xml_node_t temp_node;
	BOOL found = FALSE;
	BOOL continue_parsing = FALSE;

	// search for <definition> node in children
	continue_parsing = xml_get_first_child(node, &definition_node);
	while (continue_parsing) {
		xml_get_node_name(&definition_node, type_name_buffer, (sizeof(type_name_buffer) - 1));
		if (strcmp(type_name_buffer, "definition") == 0) {
			found = TRUE;
			break;
		} else {
			continue_parsing = xml_get_next_sibling(&definition_node, &temp_node);
			definition_node = temp_node;
		}
	}

	if (!found) {
		// <definition> node not found in <type> schema
		printf("Error! Definition node not found\n");
		return NULL;
	}

	continue_parsing = xml_get_first_child(&definition_node, &temp_node);
	while (continue_parsing) {
		
		// check for array
		uint16_t i = 0;
		uint16_t array_len = 0;
		xml_get_node_attribute(&temp_node, (uint8_t *)"elems", attr_name_buf, (sizeof(attr_name_buf) - 1), "1");
		array_len = NAME_TO_NUMERIC(attr_name_buf);

		uint16_t sub_type_len = 0;
		xml_get_node_name(&temp_node, type_name_buffer, (sizeof(type_name_buffer) - 1));
		if (strcmp(type_name_buffer, "basic") == 0) {
			// basic type. Get length from the len attribute
			xml_get_node_attribute(&temp_node, (uint8_t *)"len", attr_name_buf, (sizeof(attr_name_buf) - 1), "0");
			sub_type_len = NAME_TO_NUMERIC(attr_name_buf);
			
			// Check the default value
			size_t def_value = 0;
			xml_get_node_attribute(&temp_node, (uint8_t *)"def_value", attr_name_buf, (sizeof(attr_name_buf) - 1), "0");
			def_value = NAME_TO_NUMERIC(attr_name_buf);

			uint8_t *ptr = frame_buf;
			uint8_t offset = *bit_offset;

			for (i = 0; i < array_len; i++)
			{
				// Fill the node data in the buffer
				#warning: DATA FILLING NOT IMPLEMENTED

			}
		} else {
			uint8_t *ptr = frame_buf;
			for (i = 0; i < array_len; i++) {
				ptr = append_type_data_to_buffer(&temp_node, ptr, bit_offset);
			}
			frame_buf = ptr;
		}

		// Check for next field in the type
		xml_node_t next_node;
		continue_parsing = xml_get_next_sibling(&temp_node, &next_node);
		temp_node = next_node;
	}

	return len;
}


uint8_t *build_frame_from_schema(const uint8_t *base_type)
{
	xml_node_t type_node;
	uint16_t frame_len = 0;

	if (!get_type_node_from_name(base_type, &type_node)) {
		return NULL;
	}

	frame_len = get_type_length(&type_node);	// frame_len is in bits

	// bits to bytes
	if (frame_len & 0x07) {
		printf("Error! Length of frame not in integral bytes: %s\n", base_type);
		return NULL;
	}

	frame_len /= 8;	// convert to bytes

	uint8_t *frame_buf = MM_ALLOC(frame_len);
	uint16_t offset = 0;
	append_type_data_to_buffer(&type_node, frame_buf, &offset);
	return frame_buf;
}

uint16_t get_sub_type_offset(const uint8_t *base_type, const uint8_t *sub_type)
{
	#error "Still not complete"
}


void set_frame_element(const uint8_t *param, const uint8_t *type_name, uint8_t *bin_frame, uint32_t value);
uint32_t get_frame_element(const uint8_t *param, const uint8_t *type_name, uint8_t *bin_frame);
